
import pprint

try:
    # Test for issue 14998 <http://bugs.python.org/issue14998>.
    pprint.pformat({(None,): 0, (1,): 0})
except TypeError:
    class _safe_key(object):
        __slots__ = ['obj']

        def __init__(self, obj):
            self.obj = obj

        def __lt__(self, other):
            try:
                rv = self.obj.__lt__(other.obj)
            except TypeError:  # <- From monkey patch!
                rv = NotImplemented

            if rv is NotImplemented:
                rv = (str(type(self.obj)), id(self.obj)) < \
                     (str(type(other.obj)), id(other.obj))
            return rv
    try:
        pprint._safe_key  # New in 3.1
        pprint._safe_key = _safe_key  # Monkey patch if exists.
    except AttributeError:
        # The following code was been adapted from the Python 3.0
        # standard library (pprint.py)
        import sys as _sys
        from io import StringIO as _StringIO
        _commajoin = ", ".join
        _id = id
        _len = len
        _type = type
        def _safe_repr(object, context, maxlevels, level):
            typ = _type(object)
            if typ is str:
                if 'locale' not in _sys.modules:
                    return repr(object), True, False
                if "'" in object and '"' not in object:
                    closure = '"'
                    quotes = {'"': '\\"'}
                else:
                    closure = "'"
                    quotes = {"'": "\\'"}
                qget = quotes.get
                sio = _StringIO()
                write = sio.write
                for char in object:
                    if char.isalpha():
                        write(char)
                    else:
                        write(qget(char, repr(char)[1:-1]))
                return ("%s%s%s" % (closure, sio.getvalue(), closure)), True, False

            r = getattr(typ, "__repr__", None)
            if issubclass(typ, dict) and r is dict.__repr__:
                if not object:
                    return "{}", True, False
                objid = _id(object)
                if maxlevels and level >= maxlevels:
                    return "{...}", False, objid in context
                if objid in context:
                    return _recursion(object), False, True
                context[objid] = 1
                readable = True
                recursive = False
                components = []
                append = components.append
                level += 1
                saferepr = _safe_repr
                items = object.items()
                try:
                    items = sorted(items)
                except TypeError:
                    items = sorted(items, key=_safe_key)  # <- Insert _safe_key.
                    #def sortkey(item):
                    #    key, value = item
                    #    return str(type(key)), key, value
                    #items = sorted(items, key=sortkey)
                for k, v in items:
                    krepr, kreadable, krecur = saferepr(k, context, maxlevels, level)
                    vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level)
                    append("%s: %s" % (krepr, vrepr))
                    readable = readable and kreadable and vreadable
                    if krecur or vrecur:
                        recursive = True
                del context[objid]
                return "{%s}" % _commajoin(components), readable, recursive

            if (issubclass(typ, list) and r is list.__repr__) or \
               (issubclass(typ, tuple) and r is tuple.__repr__):
                if issubclass(typ, list):
                    if not object:
                        return "[]", True, False
                    format = "[%s]"
                elif _len(object) == 1:
                    format = "(%s,)"
                else:
                    if not object:
                        return "()", True, False
                    format = "(%s)"
                objid = _id(object)
                if maxlevels and level >= maxlevels:
                    return format % "...", False, objid in context
                if objid in context:
                    return _recursion(object), False, True
                context[objid] = 1
                readable = True
                recursive = False
                components = []
                append = components.append
                level += 1
                for o in object:
                    orepr, oreadable, orecur = _safe_repr(o, context, maxlevels, level)
                    append(orepr)
                    if not oreadable:
                        readable = False
                    if orecur:
                        recursive = True
                del context[objid]
                return format % _commajoin(components), readable, recursive

            rep = repr(object)
            return rep, (rep and not rep.startswith('<')), False

        pprint._safe_repr = _safe_repr  # Monkey patch _safe_repr on Python 3.0.

from pprint import *  # Import into local namespace after patching.

